# Merise python parser and models generators.
#
# @autor: Patrice Ferlet <metal3d@gmail.com>
# @version 0.0.2
# @licence Gnu/GPL v3

# $Id: graph.py 58 2011-03-03 17:34:00Z Metal3d $
# $HeadURL: http://merisier.googlecode.com/svn/tags/0.0.2-beta1/src/merise/graph.py $

"""
Yield Graphs from parsed merise file
Graph are named as initial merise basename file suffixed by MCD or MPD
"""
import subprocess
import os, re

class Graph:
	"""
	Graph class that can create 2 kinds of graphs
	MCD (Conceptual Graph Data Model)
	MPD (Physical Graph Data Model)
	"""
	def __init__(self, parser):
		"""
		Initialize Graph from a given merise.parser object
		"""
		self.parser = parser
		self.mcddot = "dot"
		self.mpddot ="dot"
		self.mcdrank = "LR"
		self.mpdrank = "LR"

		#see options
		self.title_color = self.parser.options.color
		self.front_title_color = self.parser.options.fcolor
		(self.basename, ext) = os.path.splitext(os.path.basename(self.parser.options.filename))
		self.algo = self.parser.options.algo
		self.format = self.parser.options.imageformat
		self.fontname = self.parser.options.fontname
		#dot template
		self.template = """
			%s structs {
				rankdir="%s"
				node [shape=plaintext, fontname=%s]
				%s
				%s
				%s
			}
		"""

	def __generateMCDDef(self):
		"""
		We must to set every entities for MCD or MPD
		"""
		struct = ""
		for k,v in self.parser.defs.items():
			border = 1
			(bg, front) = (self.title_color, self.front_title_color)
			if k in self.parser.virtuals:
				border = 1
				(front, bg) = (self.title_color, self.front_title_color)
			_def = []
			_index = 0
			_def.append("<TR><TD  BGCOLOR=\"%s\"><FONT COLOR=\"%s\" >%s</FONT></TD></TR>" % ( bg, front,k))
			try:
				j=0
				for kk in self.parser.keys[k]:
					_def.append("<TR><TD BORDER=\"%s\" BGCOLOR=\"%s\" ALIGN=\"LEFT\" port=\"%s\"><FONT COLOR=\"%s\" >%s</FONT></TD></TR>" % (border, self.parser.options.fbgcolor, self.parser.getId(k,j),  self.parser.options.frontcolor, kk ))
					j+=1
			except:
				pass
			for d in v:
				if d <> "":
					if not re.search('\+',d):
						_def.append("<TR><TD BORDER=\"%s\" BGCOLOR=\"%s\" ALIGN=\"LEFT\" port=\"%s_%s\"><FONT COLOR=\"%s\" >%s</FONT></TD></TR>" % (border, self.parser.options.fbgcolor, k,d, self.parser.options.frontcolor, d))
					_index+=1
			_def = "".join(_def)
			_def ="label=<<TABLE BORDER=\"%s\" ALIGN=\"LEFT\" CELLPADDING=\"5\" CELLSPACING=\"0\">%s</TABLE>>" % (border, _def)
			struct += "%s [%s];\n" % (k,_def)

		return struct

	def generateMCD(self):
		"""
		Generate Conceptual Datas Model
		"""
		rep = self.__generateMCDDef();
		nodes = []
		done = []
		index = 0
		_node = ""
		_link = ""
		for l in self.parser.links:
			(table1, num1, link, num2 , table2, attr) = l
			#set rounded link system
			if link not in nodes:
				_node +="%s [style=rounded, shape=\"record\", label=\"{ %s }\"];\n" % (link, link)
				if len(attr):
					_attr = re.findall('\((.*)\)', attr)
					_attr = _attr[0].split(',')
					label = []
					for a in _attr:
						a=a.strip()
						a = a.split(' ')
						label.append(a[0])

					_node += "\"%s\" [shape=plaintext, label=\"%s\l\"]; \n" %  ("attr_"+link, "\l".join(label))
					_link += "\"%s\" -- \"%s\"\n" % (link, "attr_"+link)
				nodes.append(link)
			if table1+link not in done:
				_link += "\"%s\" -- %s [label=\"%s\", fontname=\"%s\"]\n" % (table1, link, num1, self.fontname)
				done.append(table1+link)
			if table2+link not in done:
				_link += "\"%s\" -- %s [label=\"%s\", fontname=\"%s\"]\n" % (link, table2, num2, self.fontname)
				done.append(table2+link)
			index+=1

		dot = self.template % ("graph", self.mcdrank,self.fontname, rep ,_node, _link)


		path = os.path.join( self.parser.options.directory, self.basename+"_MCD."+self.format )
		
		
		bgcolor= self.parser.options.bgcolor
		if self.parser.options.dropshadow:
			bgcolor = "transparent"
			
		p = subprocess.Popen(args=[self.mcddot,'-Gbgcolor='+bgcolor, '-T'+self.format, "-o",path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
		p.communicate(dot)
		p.wait()

		if self.parser.options.dropshadow:
			os.system("/usr/bin/convert "+path+' -matte \( +clone -background black -shadow '+self.parser.options.shadow+' \)'+' +swap -background "'+ self.parser.options.bgcolor+ '"' + " -mosaic " +path)

		print path+" generated"

	def generateMPD(self):
		"""
		Generate Physical Data Model
		"""
		self.parser.findCardinals()
		struct = self.__generateMCDDef()
		s = "";
		i=0
		for l in self.parser.arrows:
			(_from, _to) = l
			if self.seed(i):
				(table1, id_table1) = _from
				(table2, id_table2) = _to
				s+= "\"%s\":%s:e -> \"%s\":%s:w [arrowtail=\"dot\",arrowhead=\"normal\", fontname=\"%s\"]\n" % (table1, id_table1, table2, id_table2, self.fontname)
			else:
				(table1, id_table1) = _to
				(table2, id_table2) = _from
				s+= "\"%s\":%s:e -> \"%s\":%s:w [arrowtail=\"normal\", arrowhead=\"dot\", fontname=\"%s\"]\n" % (table1, id_table1, table2, id_table2, self.fontname)
			i+=1

		dot = self.template % ("digraph", self.mpdrank, self.fontname, struct ,s,"")

		path = os.path.join( self.parser.options.directory, self.basename+"_MPD."+self.format )
		
		bgcolor = self.parser.options.bgcolor
		if self.parser.options.dropshadow:
			bgcolor = "transparent"
			
		p = subprocess.Popen(args=[self.mpddot,'-Gbgcolor='+bgcolor ,'-Etailclip=true','-Eheadclip=true' ,'-T'+self.format, "-o",path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True )
		p.communicate(dot)
		p.wait()

		if self.parser.options.dropshadow:
			os.system("convert "+path+' -matte \( +clone -background black -shadow '+self.parser.options.shadow+' \)'+' +swap -background "'+ self.parser.options.bgcolor+ '"' + " -mosaic " +path)

		print path+" generated"

	def seed(self,i):
		"""
		Return true or false based on algorythm and given table index.
		"""
		if self.algo == "modulo":
			return i%(len(self.parser.arrows)+1/2) == 0
		elif self.algo == "divide":
			return i > len(self.parser.arrows)/2
		elif self.algo == "random":
			import random
			return random.randint(0,1)
		elif self.algo == "even":
			return i%2==0
		elif self.algo == "odd":
			return i%2!=0
		else:
			#none...
			return False
